package com.jielihaofeng.chat.handler;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.jielihaofeng.chat.constants.MessageTypeEnum;
import com.jielihaofeng.chat.pojo.WsMessageType;
import com.jielihaofeng.chat.service.impl.ChatService;
import com.jielihaofeng.chat.vo.ChatMessageVO;
import com.jielihaofeng.chat.vo.WebSocketMessageVO;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

/**
 * 服务端自定义处理入站消息
 *
 * @author Administrator
 */
@ChannelHandler.Sharable
@Component
@Slf4j
public class WebSocketNettyHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * 存储用户对应的通道
     */
    Map<Long, List<ChannelHandlerContext>> USER_CHANNEL_MAP = new ConcurrentHashMap<>(16);

    /**
     * 存放通道到用户关联
     */
    Map<String, Long> CHANNEL_USER_MAP = new ConcurrentHashMap<>(16);

    /**
     * 存储当前连接上的通道
     */
    List<ChannelHandlerContext> LIST = new CopyOnWriteArrayList<>();

    @Autowired
    private ChatService chatService;

    public Map<Long, Boolean> getUserOnlineMap(List<Long> userIdList) {
        Map<Long, Boolean> data = new HashMap<>();
        userIdList.forEach(userId -> {
            data.put(userId, CollectionUtil.isNotEmpty(USER_CHANNEL_MAP.get(userId)));
        });
        return data;
    }

    public boolean userOnline(Long userId) {
        return CollectionUtil.isNotEmpty(USER_CHANNEL_MAP.get(userId));
    }

    /**
     * 通道连接事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        LIST.add(ctx);
        log.info("有新的连接.>>当前连接数量:{}", LIST.size());
    }

    /**
     * 通道消息事件
     *
     * @param channelHandlerContext
     * @param textWebSocketFrame
     * @throws Exception
     */
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, TextWebSocketFrame textWebSocketFrame) throws Exception {
        log.info("前端发来的消息:{}", textWebSocketFrame.text());
        WebSocketMessageVO webSocketMessageVO = JSON.parseObject(textWebSocketFrame.text(), WebSocketMessageVO.class);

        // 上线消息
        if (StringUtils.equals(webSocketMessageVO.getType(), "online")) {
            String content = webSocketMessageVO.getContent();
            log.info("user online：{}", content);
            long userId = NumberUtil.parseLong(content);
//            // 有人登录了
//            List<String> channelKey = CHANNEL_USER_MAP.entrySet().stream().filter(entry -> entry.getValue() == userId).map(Map.Entry::getKey).collect(Collectors.toList());
//            if (USER_CHANNEL_MAP.get(userId) != null|| CHANNEL_USER_MAP.values().stream().anyMatch(id->id==userId)) {
//                // 通知旧的登录下线
//                offline(userId);
//                channelKey.forEach(key->{
//                    LIST.stream().filter(context->StringUtils.equals(context.channel().id().toString(),key)).findFirst().ifPresent(context->channelInactive(USER_CHANNEL_MAP.get(userId)));
//
//                });
//            }
            setMap(channelHandlerContext, userId);
        }

        // 聊天消息
        else if (StringUtils.equals(webSocketMessageVO.getType(), "sendMessage")) {
            ChatMessageVO chatMessageVO = JSON.parseObject(webSocketMessageVO.getContent(), ChatMessageVO.class);
            String type = chatMessageVO.getType();
            MessageTypeEnum messageTypeEnum = MessageTypeEnum.getByCode(type);

            // 获取到需要转发的客户端 Message
            Long sender = chatMessageVO.getSender();
            Long receiver = chatMessageVO.getReceiver();

            // 存储消息，得到填充好的VO
            ChatMessageVO finalChatMessageVO = chatService.sendMessage(chatMessageVO);

            // 1.单聊推送
            if (messageTypeEnum == MessageTypeEnum.SINGLE) {
                log.info("{}->{}\n单聊推送：{}", sender, receiver, finalChatMessageVO);
                // 推送给发消息人（目前为了发送人展示消息，后续也需要，为了展示消息进度）和接收人
                if (Objects.equals(sender, receiver)) {
                    pushMessage(finalChatMessageVO, sender);
                } else {
                    pushMessage(finalChatMessageVO, sender, receiver);
                }
            }
            // 2.群聊推送
            else if (messageTypeEnum == MessageTypeEnum.GROUP) {
                log.info("{}->{}\n群聊推送：{}", sender, receiver, finalChatMessageVO);
                Set<Long> userIdList = chatService.getUserIdList(receiver);
                pushMessage(finalChatMessageVO, userIdList);
            }
        }


        // TODO:上线的话还是得缓存用户id和channel映射

//        if (message.getType() == 1) {
//            setMap(channelHandlerContext, message);
//            // 给其他服务器发送上线消息
//            for (ChannelHandlerContext handlerContext : USER_CHANNEL_MAP.values()) {
//                if (handlerContext == channelHandlerContext) {
//                    continue;
//                }
//                handlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
//            }
//            return;
//        }


//        // 没有指定接收者代表要群发
//        if (StringUtil.isNullOrEmpty(receive)) {
//            for (ChannelHandlerContext handlerContext : MAP.values()) {
//                if (handlerContext == channelHandlerContext) {
//                    continue;
//                }
//                handlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(chatMessageVO)));
//            }
//            return;
//        }
//        // 从缓存的存储用户对应的通道 map中获取
//        if (!MAP.containsKey(receive)) {
//            Message message1 = new Message("服务端", channelHandlerContext.name(), UUID.randomUUID().toString(), "用户未在线，你的消息不能及时送达。", 2);
//            channelHandlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message1)));
//            return;
//        }
//        // 服务端转发消息到指定的客户端
//        ChannelHandlerContext channelHandlerContext1 = MAP.get(receive);
//        channelHandlerContext1.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(chatMessageVO)));
    }

    /**
     * 消息推送单人
     *
     * @param chatMessageVO 消息内容
     * @param receiver      接收人
     */
    public void pushMessage(ChatMessageVO chatMessageVO, Long receiver) {
        List<ChannelHandlerContext> contextList = USER_CHANNEL_MAP.get(receiver);
        if (CollectionUtil.isNotEmpty(contextList)) {
            // 用户在线，使用ws服务端推消息
            contextList.forEach(context -> context.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(chatMessageVO))));
        }
        log.info("{}用户推送结果：{}", receiver, CollectionUtil.isEmpty(contextList) ? "[推送失败]用户不在线" : "[推送成功]用户在线");
    }

    /**
     * 消息推送多人
     *
     * @param chatMessageVO 消息内容
     * @param receiverSet   接收人
     */
    private void pushMessage(ChatMessageVO chatMessageVO, Set<Long> receiverSet) {
        receiverSet.forEach(receiver -> pushMessage(chatMessageVO, receiver));
    }

    /**
     * 通知下线
     *
     * @param context
     */
    private void offline(ChannelHandlerContext context) {
        if (context != null) {
            // 用户在线，使用ws服务端推消息
            JSONObject data = new JSONObject();
            data.put("type", WsMessageType.offline);
            context.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(data)));
        }
    }

    /**
     * 消息推送多人
     *
     * @param chatMessageVO 消息内容
     * @param receivers     接收人
     */
    public void pushMessage(ChatMessageVO chatMessageVO, Long... receivers) {
        for (Long receiver : receivers) {
            pushMessage(chatMessageVO, receiver);
        }
    }

    /**
     * 设置连接映射
     *
     * @param channelHandlerContext
     * @param userId
     */
    private void setMap(ChannelHandlerContext channelHandlerContext, Long userId) {
        List<ChannelHandlerContext> contextList = USER_CHANNEL_MAP.get(userId);
        if (CollectionUtil.isEmpty(contextList)) {
            contextList = new CopyOnWriteArrayList<>();
        }
        contextList.add(channelHandlerContext);
        USER_CHANNEL_MAP.put(userId, contextList);
        CHANNEL_USER_MAP.put(channelHandlerContext.channel().id().toString(), userId);
    }

    /**
     * 通达关闭事件
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        Long s = CHANNEL_USER_MAP.get(ctx.channel().id().toString());
        List<ChannelHandlerContext> contextList = USER_CHANNEL_MAP.get(s);
        contextList.remove(ctx);
        USER_CHANNEL_MAP.put(s, contextList);
        // 给其他在线用户发送该用户离线的信息
//        for (ChannelHandlerContext handlerContext : USER_CHANNEL_MAP.values()) {
//            Message message = new Message("服务端", null, UUID.randomUUID().toString(), "用户--" + s + "--已经离线了", 2);
//            handlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
//        }
        LIST.remove(ctx);
        CHANNEL_USER_MAP.remove(ctx.channel().id().toString());
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        Long s = CHANNEL_USER_MAP.get(ctx.channel().id().toString());
        USER_CHANNEL_MAP.remove(s);
        // 给其他在线用户发送该用户离线的信息
//        for (ChannelHandlerContext handlerContext : USER_CHANNEL_MAP.values()) {
//            Message message = new Message("服务端", null, UUID.randomUUID().toString(), "用户--" + s + "--连接发生问题，已被迫离线了", 2);
//            handlerContext.writeAndFlush(new TextWebSocketFrame(JSON.toJSONString(message)));
//        }
        LIST.remove(ctx);
        CHANNEL_USER_MAP.remove(ctx.channel().id().toString());
    }
}
